home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Topik / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].zip / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].adf / Source / MRDates.c < prev    next >
C/C++ Source or Header  |  1989-04-19  |  15KB  |  511 lines

  1. /*
  2.     MRDates - AmigaDOS date support routines.
  3.     07/03/88
  4.  
  5.     This package is a hybrid of code from Thad Floryan, Doug Merrit and
  6.     myself.  I wanted a reliable set of AmigaDOS date conversion routines
  7.     and this combination seems to work pretty well.  The star of the show
  8.     here is Thad's algorithm for converting the "days elapsed" field of
  9.     an AmigaDOS DateStamp, using an intermediate Julian date format.  I
  10.     lifted/embellished some of the data structures from Doug's ShowDate
  11.     package and wrote the DateToDS function.
  12.  
  13.     History:    (most recent change first)
  14.  
  15.     12/31/88 -MRR-
  16.         StrToDS was not handling the null string properly.
  17.  
  18.     11/01/88 -MRR- Added Unix-style documentation.
  19.  
  20.     07/03/88 - Changed some names:
  21.                   Str2DS => StrToDS
  22.                   DS2Str => DSToStr
  23.  */
  24.  
  25. #define MRDATES
  26. #include "MRDates.h"
  27. #include <exec/types.h>
  28. #include <ctype.h>
  29.  
  30.  
  31. char *index();
  32.  
  33. #define DATE_SEPARATORS     "/:-."
  34. #define MINS_PER_HOUR       60
  35. #define SECS_PER_MIN        60
  36. #define SECS_PER_HOUR       (SECS_PER_MIN * MINS_PER_HOUR)
  37. #define TICS_PER_SEC        50
  38.  
  39. #define YEARS_PER_CENTURY   100
  40.  
  41.  
  42. /*
  43.    definitions to calculate current date
  44.  */
  45. #define FEB             1   /* index of feb. in table (for leap years) */
  46. #define DAYS_PER_WEEK   7
  47. #define DAYS_PER_YEAR   365
  48. #define YEARS_PER_LEAP  4
  49. #define START_YEAR      1978
  50. #define FIRST_LEAP_YEAR 1980
  51. #define LEAP_ADJUST     (FIRST_LEAP_YEAR - START_YEAR)
  52. #define LEAP_FEB_DAYS   29
  53. #define NORM_FEB_DAYS   28
  54. #define IsLeap(N)       (((N) % YEARS_PER_LEAP) ? 0 : 1)
  55.  
  56.  
  57. /*  FUNCTION
  58.         DSToDate - convert a DateStamp to a DATE.
  59.  
  60.     SYNOPSIS
  61.         int DSToDate(dateStamp, date)
  62.             struct DateStamp *dateStamp;
  63.             DATE *date;
  64.  
  65.     DESCRIPTION
  66.       Extracts the date components from an AmigaDOS datestamp.
  67.       The calculations herein use the following assertions:
  68.  
  69.       146097 = number of days in 400 years per 400 * 365.2425 = 146097.00
  70.        36524 = number of days in 100 years per 100 * 365.2425 =  36524.25
  71.         1461 = number of days in   4 years per   4 * 365.2425 =   1460.97
  72.  
  73.     AUTHOR
  74.       Thad Floryan, 12-NOV-85
  75.       Mods by Mark Rinfret, 04-JUL-88
  76.  
  77.     SEE ALSO
  78.         Include file MRDates.h.
  79.  
  80.  */
  81.  
  82. #define DDELTA 722449   /* days from Jan.1,0000 to Jan.1,1978 */
  83.  
  84. static int mthvec[] =
  85.    {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
  86.  
  87. int
  88. DSToDate(ds, date)
  89.     long *ds; DATE *date;
  90.  
  91. {
  92.  
  93.     long jdate, day0, day1, day2, day3;
  94.     long year, month, day, temp;
  95.  
  96.     jdate = ds[0] + DDELTA;      /* adjust internal date to Julian */
  97.  
  98.     year = (jdate / 146097) * 400;
  99.     day0  = day1 = jdate %= 146097;
  100.     year += (jdate / 36524) * 100;
  101.     day2  = day1 %= 36524;
  102.     year += (day2 / 1461) * 4;
  103.     day3  = day1 %= 1461;
  104.     year += day3 / 365;
  105.     month = 1 + (day1 %= 365);
  106.     day = month % 30;
  107.     month /= 30;
  108.  
  109.     if ( ( day3 >= 59 && day0 < 59 ) ||
  110.         ( day3 <  59 && (day2 >= 59 || day0 < 59) ) )
  111.       ++day1;
  112.  
  113.     if (day1 > mthvec[1 + month]) ++month;
  114.     day = day1 - mthvec[month];
  115.     date->Dyear = year;
  116.     date->Dmonth = month;
  117.     date->Dday = day;
  118.     date->Dweekday = ds[0] % DAYS_PER_WEEK;
  119.  
  120.     temp = ds[1];               /* get ds_Minute value */
  121.     date->Dhour = temp / MINS_PER_HOUR;
  122.     date->Dminute = temp % MINS_PER_HOUR;
  123.     date->Dsecond = ds[2] / TICS_PER_SEC;
  124.     return 0;
  125. }
  126.  
  127. /*  FUNCTION
  128.         DateToDS(date, dateStamp)
  129.  
  130.     SYNOPSIS
  131.         void DateToDS(date, dateStamp)
  132.              DATE *date;
  133.              struct DateStamp *dateStamp;
  134.  
  135.     DESCRIPTION
  136.         DateToDS converts the special DATE format to a DateStamp.
  137.  */
  138.  
  139. DateToDS(date, ds)
  140.     DATE *date; long *ds;
  141. {
  142.     long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear;
  143.     int month;
  144.  
  145.     /* Note the special handling for year < START_YEAR.  In this case,
  146.      * the other fields are not even checked - the user just gets the
  147.      * "start of time".
  148.      */
  149.     if ((thisYear = date->Dyear) < START_YEAR) {
  150.         ds[0] = ds[1] = ds[2] = 0;
  151.         return;
  152.     }
  153.     if (IsLeap(thisYear))
  154.         calendar[FEB].Mdays = LEAP_FEB_DAYS;
  155.  
  156.     thisDay = date->Dday - 1;
  157.     thisMonth = date->Dmonth -1;
  158.     yearsElapsed = thisYear - START_YEAR;
  159.     leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP;
  160.     daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears;
  161.     for (month = 0; month < thisMonth; ++month)
  162.         daysElapsed += calendar[month].Mdays;
  163.     daysElapsed += thisDay;
  164.     calendar[FEB].Mdays = NORM_FEB_DAYS;
  165.     ds[0] = daysElapsed;
  166.     ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute;
  167.     ds[2] = date->Dsecond * TICS_PER_SEC;
  168. }
  169. /*  FUNCTION
  170.         CompareDS - compare two DateStamp values.
  171.  
  172.     SYNOPSIS
  173.         int CompareDS(date1, date2)
  174.             struct DateStamp *date1, *date2;
  175.  
  176.     DESCRIPTION
  177.         CompareDS performs an ordered comparison between two DateStamp
  178.         values, returning the following result codes:
  179.  
  180.             -1 => date1 < date2
  181.              0 => date1 == date2
  182.              1 => date1 > date2
  183.  
  184.     NOTE:
  185.         This routine makes an assumption about the DateStamp structure,
  186.         specifically that it can be viewed as an array of 3 long integers
  187.         in days, minutes and ticks order.
  188.  */
  189.  
  190. int
  191. CompareDS(d1, d2)
  192.     long *d1, *d2;
  193. {
  194.     USHORT i;
  195.     long compare;
  196.  
  197.     for (i = 0; i < 3; ++i) {
  198.         if (compare = (d1[i] - d2[i])) {
  199.             if (compare < 0) return -1;
  200.             return 1;
  201.         }
  202.     }
  203.     return 0;                       /* dates match */
  204. }
  205.  
  206. /*  FUNCTION
  207.         DSToStr - convert a DateStamp to a formatted string.
  208.  
  209.     SYNOPSIS
  210.         void DSToStr(str,fmt,d)
  211.              char *str, *fmt;
  212.              struct DateStamp *d;
  213.  
  214.     DESCRIPTION
  215.         DSToStr works a little like sprintf.  It converts a DateStamp
  216.         to an ascii formatted string.  The formatting style is dependent
  217.         upon the contents of the format string, fmt, passed to this
  218.         function.
  219.  
  220.         The content of the format string is very similar to that
  221.         for printf, with the exception that the following letters
  222.         have special significance:
  223.             y => year minus 1900
  224.             Y => full year value
  225.             m => month value as integer
  226.             M => month name
  227.             d => day of month (1..31)
  228.             D => day name ("Monday".."Sunday")
  229.             h => hour in twenty-four hour notation
  230.             H => hour in twelve hour notation
  231.             i => 12 hour indicator for H notation (AM or PM)
  232.             I => same as i
  233.             n => minutes    (sorry...conflict with m = months)
  234.             N => same as n
  235.             s => seconds
  236.             S => same as s
  237.  
  238.         All other characters are passed through as part of the normal
  239.         formatting process.  The following are some examples with
  240.         Saturday, July 18, 1987, 13:53 as an input date:
  241.  
  242.             "%y/%m/%d"          => 87/7/18
  243.             "%02m/%02d/%2y"     => 07/18/87
  244.             "%D, %M %d, %Y"     => Saturday, July 18, 1987
  245.             "%02H:%02m i"       => 01:53 PM
  246.             "Time now: %h%m"    => Time now: 13:53
  247.  
  248.  */
  249. void
  250. DSToStr(str,fmt,d)
  251.     char *str, *fmt; long *d;
  252. {
  253.     DATE date;
  254.     char fc,*fs,*out;
  255.     USHORT ivalue;
  256.     char new_fmt[256];          /* make it big to be "safe" */
  257.     USHORT new_fmt_lng;
  258.     char *svalue;
  259.  
  260.     DSToDate(d, &date);         /* convert DateStamp to DATE format */
  261.  
  262.     *str = '\0';                /* insure output is empty */
  263.     out = str;
  264.     fs = fmt;                   /* make copy of format string pointer */
  265.  
  266.     while (fc = *fs++) {        /* get format characters */
  267.         if (fc == '%') {        /* formatting meta-character? */
  268.             new_fmt_lng = 0;
  269.             new_fmt[new_fmt_lng++] = fc;
  270.             /* copy width information */
  271.             while (isdigit(fc = *fs++) || fc == '-')
  272.                 new_fmt[new_fmt_lng++] = fc;
  273.  
  274.             switch (fc) {       /* what are we trying to do? */
  275.             case 'y':           /* year - 1980 */
  276.                 ivalue = date.Dyear % 100;
  277. write_int:
  278.                 new_fmt[new_fmt_lng++] = 'd';
  279.                 new_fmt[new_fmt_lng] = '\0';
  280.                 sprintf(out,new_fmt,ivalue);
  281.                 out = str + strlen(str);
  282.                 break;
  283.             case 'Y':           /* full year value */
  284.                 ivalue = date.Dyear;
  285.                 goto write_int;
  286.  
  287.             case 'm':           /* month */
  288.                 ivalue = date.Dmonth;
  289.                 goto write_int;
  290.  
  291.             case 'M':           /* month name */
  292.                 svalue = calendar[date.Dmonth - 1].Mname;
  293. write_str:
  294.                 new_fmt[new_fmt_lng++] = 's';
  295.                 new_fmt[new_fmt_lng] = '\0';
  296.                 sprintf(out,new_fmt,svalue);
  297.                 out = str + strlen(str);
  298.                 break;
  299.  
  300.             case 'd':           /* day */
  301.                 ivalue = date.Dday;
  302.                 goto write_int;
  303.  
  304.             case 'D':           /* day name */
  305.                 svalue = dayNames[d[0] % DAYS_PER_WEEK];
  306.                 goto write_str;
  307.  
  308.             case 'h':           /* hour */
  309.                 ivalue = date.Dhour;
  310.                 goto write_int;
  311.  
  312.             case 'H':           /* hour in 12 hour notation */
  313.                 ivalue = date.Dhour;
  314.                 if (ivalue >= 12) ivalue -= 12;
  315.                 goto write_int;
  316.  
  317.             case 'i':           /* AM/PM indicator */
  318.             case 'I':
  319.                 if (date.Dhour >= 12)
  320.                     svalue = "PM";
  321.                 else
  322.                     svalue = "AM";
  323.                 goto write_str;
  324.  
  325.             case 'n':           /* minutes */
  326.             case 'N':
  327.                 ivalue = date.Dminute;
  328.                 goto write_int;
  329.  
  330.             case 's':           /* seconds */
  331.             case 'S':
  332.                 ivalue = date.Dsecond;
  333.                 goto write_int;
  334.  
  335.             default:
  336.                 /* We are in deep caca - don't know what to do with this
  337.                  * format character.  Copy the raw format string to the
  338.                  * output as debugging information.
  339.                  */
  340.                 new_fmt[new_fmt_lng++] = fc;
  341.                 new_fmt[new_fmt_lng] = '\0';
  342.                 strcat(out, new_fmt);
  343.                 out = out + strlen(out);    /* advance string pointer */
  344.                 break;
  345.             }
  346.         }
  347.         else
  348.             *out++ = fc;        /* copy literal character */
  349.     }
  350.     *out = '\0';                /* terminating null */
  351. }
  352.  
  353. /*  FUNCTION
  354.         StrToDS - convert a string to a DateStamp.
  355.  
  356.     SYNOPSIS
  357.         int StrToDS(string, date)
  358.             char *string;
  359.             struct DateStamp *date;
  360.  
  361.     DESCRIPTION
  362.         StrToDS expects its string argument to contain a date in
  363.         MM/DD/YY HH:MM:SS format.  The time portion is optional.
  364.         StrToDS will attempt to convert the string to a DateStamp
  365.         representation.  If successful, it will return 0.  On
  366.         failure, a 1 is returned.
  367.  
  368.  */
  369.  
  370. int
  371. StrToDS(str, d)
  372.     char *str; long *d;
  373. {
  374.     register char c;
  375.     int count;
  376.     int i, item;
  377.     DATE date;              /* unpacked DateStamp */
  378.     char *s;
  379.  
  380.     int values[3];
  381.     int value;
  382.  
  383.     s = str;
  384.     for (item = 0; item < 2; ++item) {  /* item = date, then time */
  385.         for (i = 0; i < 3; ++i) values[i] = 0;
  386.         count = 0;
  387.         while (c = *s++) {          /* get date value */
  388.             if (c <= ' ')
  389.                 break;
  390.  
  391.             if (isdigit(c)) {
  392.                 value = 0;
  393.                 do {
  394.                     value = value*10 + c - '0';
  395.                     c = *s++;
  396.                 } while (isdigit(c));
  397.                 if (count == 3) {
  398.     bad_value:
  399. #ifdef DEBUG
  400.                     puts("Error in date-time format.\n");
  401.                     printf("at %s: values(%d) = %d, %d, %d\n",
  402.                         s, count, values[0], values[1], values[2]);
  403. #endif
  404.                     return 1;
  405.                 }
  406.                 values[count++] = value;
  407.                 if (c <= ' ')
  408.                     break;
  409.             }
  410.             else if (! index(DATE_SEPARATORS, c) )
  411.                 goto bad_value;     /* Illegal character - quit. */
  412.         }                           /* end while */
  413.         if (item) {                 /* Getting time? */
  414.             date.Dhour = values[0];
  415.             date.Dminute = values[1];
  416.             date.Dsecond = values[2];
  417.         }
  418.         else {                      /* Getting date? */
  419.  
  420. /* It's OK to have a null date string, but it's not OK to specify only
  421.    1 or 2 of the date components.
  422.  */
  423.             if (count && count != 3)
  424.                 goto bad_value;
  425.             date.Dmonth = values[0];
  426.             date.Dday = values[1];
  427.             date.Dyear = values[2];
  428.             if (date.Dyear == 0) {
  429.                 date.Dyear = START_YEAR;
  430.                 date.Dday = 1;
  431.             }
  432.             else {
  433.                 if (date.Dyear < (START_YEAR - 1900) )
  434.                     date.Dyear += 100;
  435.                 date.Dyear += 1900;
  436.             }
  437.         }
  438.     }                               /* end for */
  439.     DateToDS(&date, d);
  440.     return 0;
  441. }                                   /* StrToDS */
  442.  
  443.  
  444. #ifdef DEBUG
  445. #include "stdio.h"
  446. main(ac, av)
  447.     int ac;
  448.     char    **av;
  449. {
  450.     long    datestamp[3];           /* A little dangerous with Aztec */
  451.     long    datestamp2[3];
  452.     DATE    date, oldDate;
  453.     long    day, lastDay;
  454.     int     errors = 0;
  455.  
  456.     /*
  457.      * display results from DateStamp() (hours:minutes:seconds)
  458.      */
  459.     DateStamp(datestamp);
  460.  
  461.     /*
  462.      * display results from DSToDate() (e.g. "03-May-88")
  463.      */
  464.     DSToDate(datestamp, &date);
  465.     printf("Current date: %02d-%s-%02d\n",
  466.         date.Dday, calendar[ date.Dmonth - 1].Mname,
  467.         (date.Dyear % YEARS_PER_CENTURY));
  468.  
  469.     printf("Current time: %02d:%02d:%02d\n",
  470.         date.Dhour, date.Dminute, date.Dsecond);
  471.  
  472.     printf("\nDoing sanity check through year 2000...\n\t");
  473.     lastDay = (2000L - START_YEAR) * 365L;
  474.     lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP;
  475.     for (day = 0; day <= lastDay; ++day) {
  476.         if (day % 1000 == 0) {
  477.             printf(" %ld", day);
  478.             fflush(stdout);
  479.         }
  480.         datestamp[0] = day;
  481.         datestamp[1] = MINS_PER_HOUR - 1;
  482.         datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1);
  483.         DSToDate(datestamp, &date);
  484.         if (day && date == oldDate) {
  485.             printf("Got same date for days %d, %d: %02d-%s-%02d\n",
  486.                     day - 1, day,
  487.                     date.Dday,
  488.                     calendar[ date.Dmonth - 1 ].Mname,
  489.                     (date.Dyear % YEARS_PER_CENTURY));
  490.  
  491.             if (++errors == 10)
  492.                 exit(1);
  493.         }
  494.         DateToDS(&date, datestamp2);
  495.         if (day != datestamp2[0]) {
  496.             printf("\nConversion mismatch at day %ld!\n", day);
  497.             printf("\tBad value = %ld", datestamp2[0]);
  498.             printf("\tDate: %02d-%s-%02d\n",
  499.                     date.Dday,
  500.                     calendar[ date.Dmonth  -1 ].Mname,
  501.                     (date.Dyear % YEARS_PER_CENTURY));
  502.             if (++errors == 10)
  503.                 exit(1);
  504.         }
  505.         oldDate = date;
  506.     }
  507.     printf("\nSanity check passed.\n");
  508. } /* main() */
  509. #endif
  510.  
  511.